/*
 * @Title: 贝塞尔曲线绘制
 * @Author: 沈童童
 * @Date: 2018-11-12 19:06:46
 * @LastEditTime: 2021-12-03 17:16:13
 */
/**
 * @param {Object} targetPositionXY 终点目标位置
 * @param {number} pointsAmount 绘制的点的个数  默认：30
 * @param {obj} touchDomXY 点击时event事件中的坐标 x, y
 * @param {number} throwHeight 小球从起始位置向上抛的相对高度，此值越大，抛的越高，默认250
 * @param {function} setBallPosition 此函数为绘制点线时执行的函数，一版用此函数来控制小球相对屏幕定位中top、left;
 * @retun { Promise } 此函数执行完一次抛物线后返回一个promise
 */
interface Coordinate {
  x: number;
  y: number;
}

interface ConstructorProps {
  targetPositionXY: Coordinate;
  touchDomXY: Coordinate;
  pointsAmount?: number;
  throwHeight?: number;
  setBallPosition: Function;
  speed?: number;
}

 class Bezier {
    targetPositionXY: Coordinate;
    pointsAmount: number;
    throwHeight: number;
    touchDomXY: Coordinate;
    setBallPosition: Function;
    draw: Function;
    MultiPointBezier: Function;
    bezierPoints: Array<Coordinate>;
    speed: number;

    constructor (args: ConstructorProps) {
      const {targetPositionXY, pointsAmount = 10, throwHeight = 250, touchDomXY, setBallPosition, speed = 30} = args

      this.targetPositionXY = targetPositionXY
      this.touchDomXY = touchDomXY
      this.setBallPosition = setBallPosition
      this.pointsAmount = pointsAmount
      this.throwHeight = throwHeight
      this.speed = speed
      /**
       * @description 贝塞尔曲线"点"的绘制
       */
      this.draw = (anchorpoints: Array<Coordinate>) : Array<Coordinate> => {
        var points: Array<Coordinate> = []
        for (var i = 0; i < this.pointsAmount; i++) {
          var point = this.MultiPointBezier(anchorpoints, i / this.pointsAmount)
          points.push(point)
        }
        return points
      }
      /**
       * @param  {Array} points [目标点，移动的最高点，起始点]
       */
      this.MultiPointBezier = function (points: Array<Coordinate>, t: number): Coordinate {
        var len = points.length
        var x = 0
        var y = 0
        var erxiangshi = function (start: number, end: number) {
          var cs = 1
          var bcs = 1
          while (end > 0) {
            cs *= start
            bcs *= end
            start--
            end--
          }
          return cs / bcs
        }
        for (var i = 0; i < len; i++) {
          var point = points[i]
          x +=
          point.x *
          Math.pow(1 - t, len - 1 - i) *
          Math.pow(t, i) *
          erxiangshi(len - 1, i)
          y +=
          point.y *
          Math.pow(1 - t, len - 1 - i) *
          Math.pow(t, i) *
          erxiangshi(len - 1, i)
        }
        return { x: x, y: y }
      }
    }
    /**
     * @description 生成最终所需要的贝塞尔曲线上的所有的点
     */
    generatePoints () {
      var topPoint = {}
      if (this.touchDomXY['y'] < this.targetPositionXY['y']) {
        topPoint['y'] = this.touchDomXY['y'] - this.throwHeight
      } else {
        topPoint['y'] = this.targetPositionXY['y'] - this.throwHeight
      }
      topPoint['x'] =
        Math.abs(this.touchDomXY['x'] - this.targetPositionXY['x']) / 2

      if (this.touchDomXY['x'] > this.targetPositionXY['x']) {
        topPoint['x'] =
          (this.touchDomXY['x'] - this.targetPositionXY['x']) / 2 +
          this.targetPositionXY['x']
      } else {
        topPoint['x'] =
          (this.targetPositionXY['x'] - this.touchDomXY['x']) / 2 +
          this.touchDomXY['x']
      }

      this.bezierPoints = this.draw([this.targetPositionXY, topPoint, this.touchDomXY])
    }

    doMove () {
      return new Promise((resolve) => {
        this.generatePoints()
        const that = this

        let curCoordIdx = this.bezierPoints.length - 1

        const firstPositon = {
          left:  this.touchDomXY['x'] - 10,
          top:  this.touchDomXY['y'] - 10
        }

        this.setBallPosition({ ...firstPositon })

        function setStyleByFrame() {
            if (curCoordIdx >= 0) {
              that.setBallPosition({
                left: that.bezierPoints[curCoordIdx]['x'],
                top: that.bezierPoints[curCoordIdx]['y']
              })

              curCoordIdx -= 1

              requestAnimationFrame(setStyleByFrame)
            } else {
              resolve('moveFinish')
            }
        }
        requestAnimationFrame(setStyleByFrame)
      })
    }
  }

  export default Bezier
